﻿using DataService.Cache.Data;
using DataService.Cache.IService;
using System;

namespace DataService.Data
{
    /// <summary>雪花算法。分布式Id，【业务内必须确保一个实例，一个线程内使用一个实例】</summary>
    /// <remarks>
    /// 使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛，且ID 引入了时间戳，基本上保持自增。
    /// 1bit保留 + 41bit时间戳 + 10bit机器 + 12bit序列号
    /// 
    /// 内置自动选择机器workerId，IP+进程+线程，无法绝对保证唯一，从而导致整体生成的雪花Id有一定几率重复。
    /// 如果想要绝对唯一，建议在外部设置唯一的workerId，再结合单例使用，此时确保最终生成的Id绝对不重复！
    /// 高要求场合，推荐使用Redis自增序数作为workerId，在大型分布式系统中亦能保证绝对唯一。
    /// 已提供JoinCluster方法，用于把当前对象加入集群，确保workerId唯一。
    /// </remarks>
    public class Snowflake
    {

        /// <summary>开始时间戳。首次使用前设置，否则无效，默认2023-1-1</summary>
        public DateTime StartTimestamp { get; set; } = new DateTime(2022, 1, 1);

        /// <summary>机器Id，取10位。内置默认取IP+进程+线程，不能保证绝对唯一，要求高的场合建议外部保证workerId唯一</summary>
        public Int32 WorkerId { get; set; }

        /// <summary>全局机器Id。若设置，所有雪花实例都将使用该Id，可以全局变量提供本应用全局唯一机器码，且跨多环境唯一</summary>
        public static Int32 GlobalWorkerId { get; set; }

        /// <summary>workerId分配集群。配置后可确保所有实例化的雪花对象得到唯一workerId，建议使用Redis</summary>
        public static ICache Cluster { get; set; }


        private SnowflakeCore _SnowflakeCore;
        public Snowflake()
        {
            _SnowflakeCore = new SnowflakeCore();
            _SnowflakeCore.StartTimestamp = StartTimestamp;
            _SnowflakeCore.WorkerId = WorkerId;
            _SnowflakeCore.GlobalWorkerId = GlobalWorkerId;
            _SnowflakeCore.Cluster = Cluster;
        }
        /// <summary>
        /// 获取下一个Id
        /// </summary>
        public Int64 NewId()
        {
            return _SnowflakeCore.NewId();
        }
        public Int64 NewId(int workerId)
        {
            _SnowflakeCore.WorkerId = workerId;
            return _SnowflakeCore.NewId();
        }
        /// <summary>获取指定时间的Id，带上节点和序列号。可用于根据业务时间构造插入Id</summary>
        /// <param name="time">时间</param>
        /// <returns></returns>
        public Int64 NewId(DateTime time)
        {
            return _SnowflakeCore.NewId(time);
        }

        /// <summary>
        /// 时间转为Id，不带节点和序列号。可用于构建时间片段查询;
        /// 例如 Select * from Order where Id>=GetId(start) and Id<GetId(end);
        /// </summary>
        /// <param name="time">时间</param>
        /// <returns></returns>
        public Int64 GetId(DateTime time)
        {
            return _SnowflakeCore.GetId(time);
        }

        /// <summary>尝试分析</summary>
        /// <param name="id"></param>
        /// <param name="time">时间</param>
        /// <param name="workerId">节点</param>
        /// <param name="sequence">序列号</param>
        /// <returns></returns>
        public Boolean TryParse(Int64 id, out long ms, out Int32 workerId, out Int32 sequence)
        {
            return _SnowflakeCore.TryParse(id, out ms, out workerId, out sequence);
        }
        /// <summary>
        /// 创建分表标识
        /// </summary>
        /// <param name="snowId">雪花Id</param>
        /// <returns></returns>
        public static string CreateTableNameFlag(long snowId)
        {
            return DataBaseHelper.CreateTableNameFlag(snowId);
        }

        /// <summary>
        /// 分表标识
        /// </summary>
        /// <returns></returns>
        public static string CreateTableNameFlag()
        {
            return DataBaseHelper.CreateTableNameFlag();
        }
    }
}
